#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libaudit.h>
#include <auparse.h>
#include <syslog.h>
#include "common.h"

/*
#define CIPHER_LIST "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"
#define CAFILE "/etc/banssh/rootcert.pem"
#define CADIR "/etc/banssh"
#define CERTFILE "/etc/banssh/client.pem"
*/

#define MAXLOGINS 3
#define MAXLINE 1024

extern int errno;

FILE *
Popen(const char *command, const char *mode)
{
	FILE *fp;

	if ((fp = popen(command, mode)) == NULL)
		fprintf(stderr, "popen error");
	return (fp);
}

int
Pclose(FILE * fp)
{
	int n;

	if ((n = pclose(fp)) == -1)
		fprintf(stderr, "pclose error");
	return (n);
}

char *
Fgets(char *ptr, int n, FILE * stream)
{
	char *rptr;

	if ((rptr = fgets(ptr, n, stream)) == NULL && ferror(stream))
		fprintf(stderr, "fgets error");

	return (rptr);
}

void
Fputs(const char *ptr, FILE * stream)
{
	if (fputs(ptr, stream) == EOF)
		fprintf(stderr, "fputs error");
}

SSL_CTX *
setup_client_ctx(void)
{
	SSL_CTX *ctx;

	ctx = SSL_CTX_new(SSLv23_method());
	if (SSL_CTX_load_verify_locations(ctx, CAFILE, CADIR) != 1)
		int_error("Error loading CA file and/or directory");
	if (SSL_CTX_set_default_verify_paths(ctx) != 1)
		int_error("Error loading default CA file and/or directory");
	if (SSL_CTX_use_certificate_chain_file(ctx, CERTFILE) != 1)
		int_error("Error loading certificate from file");
	if (SSL_CTX_use_PrivateKey_file(ctx, CERTFILE, SSL_FILETYPE_PEM) != 1)
		int_error("Error loading private key from file");
	SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, verify_callback);
	SSL_CTX_set_verify_depth(ctx, 4);
	SSL_CTX_set_options(ctx, SSL_OP_ALL | SSL_OP_NO_SSLv2);
	if (SSL_CTX_set_cipher_list(ctx, CIPHER_LIST) != 1)
		int_error("Error setting cipher list (no valid ciphers)");
	return ctx;
}
#ifdef CHECK_ADDR
static int
check_addr(char *s)
{
	int cnt = 0;

	while (*s != '\0' && cnt < 15) {
		if (*s >= '0' && *s <= '9') {
			s++;
		}
		else {
			if (*s == '.' || *s == ' ' || *s == '\n') {
				s++;
			}
			else {
				return -1;
			}
		}
		cnt++;
	}
	if (cnt < 7)
		return -1;
	return cnt;
}
#endif

static void
auparse_callback(auparse_state_t * au, auparse_cb_event_t cb_event_type,
		 void *user_data)
{
	int *event_cnt = (int *)user_data;
	int record_cnt;

	if (cb_event_type == AUPARSE_CB_EVENT_READY) {
		if (auparse_first_record(au) <= 0) {
			printf("can't get first record\n");
			return;
		}
		printf("event %d has %d records\n", *event_cnt,
		       auparse_get_num_records(au));
		record_cnt = 1;
		do {
			printf("    record %d of type %d(%s) has %d fields\n",
			       record_cnt,
			       auparse_get_type(au),
			       audit_msg_type_to_name(auparse_get_type(au)),
			       auparse_get_num_fields(au));
			printf("    line=%d file=%s\n",
			       auparse_get_line_number(au),
			       auparse_get_filename(au) ?
			       auparse_get_filename(au) : "None");
			const au_event_t *e = auparse_get_timestamp(au);
			if (e == NULL) {
				return;
			}
			printf("    event time: %u.%u:%lu, host=%s\n",
			       (unsigned)e->sec,
			       e->milli, e->serial, e->host ? e->host : "?");
			auparse_first_field(au);
			do {
				printf("        %s=%s (%s)\n",
				       auparse_get_field_name(au),
				       auparse_get_field_str(au),
				       auparse_interpret_field(au));
			} while (auparse_next_field(au) > 0);
			printf("\n");
			record_cnt++;
		} while (auparse_next_record(au) > 0);
		(*event_cnt)++;
	}
}

int
do_client_loop(SSL * ssl)
{
	int err, nread, len;
	char buf[1024];
	buf[0] = '\0';

	auparse_state_t *au;
	int event_cnt = 1;
	au = auparse_init(AUSOURCE_FEED, 0);
	auparse_add_callback(au, auparse_callback, &event_cnt, NULL);

	do {

		/* Maybe this is causing problems? Yeah, I think it is correct now */
		for (nread = 0; nread < (int)sizeof(buf); nread += err) {
			err = SSL_read(ssl, buf + nread, sizeof(buf) - nread);
			/*
			   fprintf(stderr,"nread(%d),err(%d),(sizeof(buf) - (nread+err))=%d\n",
			   nread,err,sizeof(buf) - (nread+err));
			 */

			if (err > 0) {
				buf[nread + err] = '\0';
				//fprintf(stderr,"%s",buf+nread);
				len = strlen(buf + nread);
				auparse_feed(au, buf + nread, len);

			}
			else {
				break;
			}

			if (event_cnt >= 1000) {

				fprintf(stderr,
					"\n***** Clearing buffer *****\n");
				auparse_flush_feed(au);
				auparse_destroy(au);
				event_cnt = 1;
				au = auparse_init(AUSOURCE_FEED, 0);
				auparse_add_callback(au, auparse_callback,
						     &event_cnt, NULL);

			}

		}
	} while (err > 0);

	auparse_flush_feed(au);
	auparse_destroy(au);
	fprintf(stderr, "We get to flush\n");

	return 1;
}

int
main_ssl(int argc, char **argv)
{
	BIO *conn;
	SSL *ssl;
	SSL_CTX *ctx;
	long err;

	init_conf();
	init_OpenSSL();
	seed_prng();

	ctx = setup_client_ctx();

	fprintf(stderr, "SERVER= %s:%s\n", SERVER, PORT);
	conn = BIO_new_connect(SERVER_PORT);
	if (!conn)
		int_error("Error creating connection BIO");

	if (BIO_do_connect(conn) <= 0)
		int_error("Error connecting to remote machine");

	ssl = SSL_new(ctx);
	SSL_set_bio(ssl, conn, conn);
	if (SSL_connect(ssl) <= 0)
		int_error("Error connecting SSL object");
	if ((err = post_connection_check(ssl, SERVER)) != X509_V_OK) {
		fprintf(stderr, "-Error: peer certificate: %s\n",
			X509_verify_cert_error_string(err));
		int_error("Error checking SSL object after connection");
	}
	fprintf(stderr, "SSL Connection opened\n");
	//if (do_client_loop(ssl))
	SSL_shutdown(ssl);
	//else
	//SSL_clear(ssl);
	fprintf(stderr, "SSL Connection closed\n");

	SSL_free(ssl);
	SSL_CTX_free(ctx);
	free_conf();
	return 0;
}

int
main(int argc, char **argv)
{
	int i = 0, j = 0;

	//daemon(0, 0);

	fprintf(stderr, "clientparse loaded\n");
	/* This for loop is is the connection gets dropped.
	   We're the client, and we need to try and connect to the server */
	for (j = 0; j < 1000; ++j) {
		for (i = 0; i < 200; ++i) {
			main_ssl(argc, argv);
			//sleep(2 * i);
			fprintf(stderr, "clientparse trying to connect\n");
		}
	}

	return 0;
}
